package com.rq.shuduapp;


import android.graphics.Point;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Calculate2 {
    private Row2[][] mayValue;
    private String rules;
    int step = 0;

    public Calculate2(Object[][] que, String rules) {
        if (que == null || rules == null) {
            return;
        }
        this.rules = rules;
        Row2.setRules(rules);
        mayValue = new Row2[rules.length()][rules.length()];
        for (int x = 0; x < que.length; x++) {
            for (int y = 0; y < que[x].length; y++) {
                if (!isEmpty(que[x][y], rules)) {
                    mayValue[x][y] = new Row2(stringObj(que[x][y]), x, y);
                } else {
                    mayValue[x][y] = new Row2("", x, y);
                }
            }
        }
    }

    public Row2 getNext() {
        for (int x = 0; x < mayValue.length; x++) {
            for (int y = 0; y < mayValue[x].length; y++) {
                Row2 res = refuse(mayValue[x][y]);
                if (res != null) {
                    res.writeTime = step++;
                    steps.add(res);
                    return res;
                }
            }
        }
        Row2 item = guessOne();
        if (item != null) {
            steps.add(item);
            guessStep.add(item);
            mayValue[item.x][item.y] = item;
            Point key = new Point(item.x, item.y);
            if (times.containsKey(key)) {
                times.put(key, times.get(key) + 1);
            } else {
                times.put(key, 1);
            }
            return item;
        }
        return null;
    }

    HashMap<Point, Integer> times = new HashMap<>();

    public void showGuessTimes() {
        LOG.e("Calculate2", "showGuessTimes");
        for (Point point : times.keySet()) {
            LOG.e("showGuessTimes", point.x + "," + point.y + "<" + mayValue[point.x][point.y].guessValueRes + "." + mayValue[point.x][point.y].value + ">-->" + times.get(point));
        }
    }

    public boolean rollBack() {
        Row2 lastGuess = findLastCanWriteGuess();
        if (lastGuess == null) return false;

//        System.out.println("=================");
//                            LOG.e("ShuDu", "130:" + steps.size());
        int lastIndex = steps.indexOf(lastGuess);
        for (int i = steps.size() - 1; i >= lastIndex; i--) {
            Row2 remove = steps.remove(i);
            mayValue[remove.getX()][remove.getY()].value = "";
        }
        lastGuess.value = lastGuess.guessValueRes.substring(lastGuess.guessIndex + 1, lastGuess.guessIndex + 2);
        lastGuess.guessIndex++;
        System.out.println("<" + lastGuess.locTxt() + ">新猜测为:" + lastGuess.value);
        guessStep.add(lastGuess);
        steps.add(lastGuess);
//                            LOG.e("ShuDu", "137:" + steps.size());
        mayValue[lastGuess.getX()][lastGuess.getY()].value = lastGuess.getValue();
//                            printAll(res);
//                            System.out.println("=================");
//        mCalculate.synchronization(res);

        return true;
    }

    private Row2 findLastCanWriteGuess() {
        if (guessStep.size() == 0) {
            System.out.println("没有猜测");
            return null;
        }
        Row2 lastGuess = guessStep.remove(guessStep.size() - 1);
        while (lastGuess == null && guessStep.size() > 0) {
            lastGuess = guessStep.remove(guessStep.size() - 1);
        }
        if (lastGuess == null) {
            System.out.println("没有更多猜测");
            return null;
        }
        if (lastGuess.guessIndex < lastGuess.guessValueRes.length() - 1) {
//                    System.out.println("寻找到可回溯:" + lastGuess.toString());
            return lastGuess;
        } else {
            return findLastCanWriteGuess();
        }
    }

    List<Row2> steps = new ArrayList<>();
    List<Row2> guessStep = new ArrayList<>();

    private Row2 guessOne() {
        return guessOne(0);
    }


    public Row2 guessOne(int index) {
        Row2 res = null;
        for (int x = 0; x < mayValue.length; x++) {
            for (int y = 0; y < mayValue[x].length; y++) {
                Row2 newRes = guess(mayValue[x][y]);
                if (newRes != null && (res == null || res.guessValueRes.length() > newRes.guessValueRes.length())) {
                    res = newRes;
                }
            }
        }
        if (res != null && res.value != null) {
            res.writeTime = step++;
            if (res.guessValueRes == null) {
                res.guessValueRes = res.value;
                res.guessIndex = index;
                res.value = res.value.substring(index, index + 1);
            } else {
                res.guessIndex = index;
                res.value = res.guessValueRes.substring(index, index + 1);
            }
        }
        return res;
    }

    public Row2 guess(Row2 check) {
        if (this.step == -1) {
            return null;
        }
        if (check.value == null || check.value.length() == 1) {
            return null;
        }
        String mV = check.guessValueRes == null ? rules : check.guessValueRes;
        int INDEX = check.getSqrIndex();
        int SQR = check.sqr;
//        StringBuffer sbKuai = new StringBuffer(INDEX + ".块排除:");
//        StringBuffer sbLieP = new StringBuffer(check.y + ".列排除:");
//        StringBuffer sbHang = new StringBuffer(check.x + ".行排除:");
        for (int i = 0; i < rules.length(); i++) {
            if (mayValue[i][check.y].value.length() == 1) {
                //同行数据相同值排除
                mV = mV.replace(mayValue[i][check.y].value, "");
//                sbLieP.append("(" + i + "," + check.y + "):" + mayValue[i][check.y].value + " ");
            }
            if (mayValue[check.x][i].value.length() == 1) {
                //同列数据相同值排除
                mV = mV.replace(mayValue[check.x][i].value, "");
//                sbHang.append("(" + check.x + "," + i + "):" + mayValue[check.x][i].value + " ");
            }
            int x = INDEX / SQR * SQR + i / SQR;
            int y = INDEX % SQR * SQR + i % SQR;
            //同块数据排除
            if (mayValue[x][y].value.length() == 1) {
                mV = mV.replace(mayValue[x][y].value, "");
//                sbKuai.append("(" + x + "," + y + "):" + mayValue[x][y].value + " ");
            }
        }
        if (mV.length() > 1) {
            check.guessValueRes = mV;
            return check;
        }
        return null;
    }

    public Row2 refuse(Row2 check) {
        if (this.step == -1) {
            return null;
        }
        if (check.value != null && check.value.length() == 1) {
            return null;
        }
        String mV = rules;
        int INDEX = check.getSqrIndex();
        int SQR = check.sqr;
//        StringBuffer sbKuai = new StringBuffer(INDEX + ".块排除:");
//        StringBuffer sbLieP = new StringBuffer(check.y + ".列排除:");
//        StringBuffer sbHang = new StringBuffer(check.x + ".行排除:");
        for (int i = 0; i < rules.length(); i++) {
            if (mayValue[i][check.y].value.length() == 1) {
                //同行数据相同值排除
                mV = mV.replace(mayValue[i][check.y].value, "");
//                sbLieP.append("(" + i + "," + check.y + "):" + mayValue[i][check.y].value + " ");
            }
            if (mayValue[check.x][i].value.length() == 1) {
                //同列数据相同值排除
                mV = mV.replace(mayValue[check.x][i].value, "");
//                sbHang.append("(" + check.x + "," + i + "):" + mayValue[check.x][i].value + " ");
            }
            int x = INDEX / SQR * SQR + i / SQR;
            int y = INDEX % SQR * SQR + i % SQR;
            //同块数据排除
            if (mayValue[x][y].value.length() == 1) {
                mV = mV.replace(mayValue[x][y].value, "");
//                sbKuai.append("(" + x + "," + y + "):" + mayValue[x][y].value + " ");
            }
        }
//        printLog(check, "\n" + sbHang.toString());
//        printLog(check, "\n" + sbLieP.toString());
//        printLog(check, "\n" + sbKuai.toString());
//        printLog(check, "\n(" + check.loc() + ")可能值:" + mV + "\n");
        if (mV.length() == 1) {
            check.value = mV;
            return check;
        } else if (mV.length() > 0) {
            this.mayValue[check.x][check.y].guessValueRes = mV;
        }
        return null;
    }

    public Row2[][] getGuessRes() {
        return mayValue;
    }

    private void printLog(String con) {
//        if (con.length() > 0) {
////            check.x == 6 && check.y == 0 &&
        System.out.print(con);
//        }
    }

    private void printLog(Row2 check, String con) {
//        if (con.length() > 0) {
////            check.x == 6 && check.y == 0 &&
//            System.out.print(con);
//        }
    }

    public int getSqrIndex(Row row) {
        return row.sqrX * row.sqr + row.sqrY;
    }

    public String stringObj(Object con) {
        if (con instanceof Row2) {
            Row2 item = (Row2) con;
            if (item.value == null) return "";
            return String.valueOf(item.value);
        }
        return String.valueOf(con);
    }

    public static boolean isEmpty(Object con, String rules) {
        try {
            return con == null
                    || con instanceof Integer && (int) con == 0
                    || con instanceof String && (((String) con).length() == 0 || !rules.contains((String) con))
                    || con instanceof CharSequence && !rules.contains((CharSequence) con)
                    || con instanceof Character && !rules.contains(String.valueOf(con));


        } catch (Exception e) {
            e.printStackTrace();
//            System.out.println(rules);
//            System.out.println(con);
            return true;
        }
    }


    public void printAll() {
        Row2[][] res = this.mayValue;
        int sqrt = (int) Math.sqrt(res.length);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < res.length; i++) {
            for (int k = 0; k < res[i].length; k++) {
                if (k == 0) sb.append(rules.charAt(i) + ":");
                sb.append(isEmpty(res[i][k], rules) ? "_" : res[i][k].value.length() == 0 ? "_" : res[i][k].value);
                if ((k + 1) % sqrt == 0) sb.append(" ");
            }
            sb.append("\n");
        }
        sb.append("====================");
        System.err.println(sb.toString());
    }

    public void synchronization(Object[][] res) {
        for (int x = 0; x < mayValue.length; x++) {
            for (int y = 0; y < mayValue[x].length; y++) {
                if (!isEmpty(res[x][y], rules)) {
                    mayValue[x][y].value = stringObj(res[x][y]);
                } else {
                    mayValue[x][y].value = "";
                    mayValue[x][y].writeTime = 0;
                }
            }
        }
//        printAll();
    }

    public boolean isOk() {
//        System.err.println("=======isOk=======");
//        printAll();
//        System.err.println("=======isOk=======");
//        System.err.println(rules);
//        System.err.println("=======isOk=======");
        return Calculate.isOk(mayValue, rules);
    }
}
